package com.hanxiaozhang.system.service.impl;

import com.hanxiaozhang.constant.CacheName;
import com.hanxiaozhang.constant.Constant;
import com.hanxiaozhang.constant.DelFlagEnum;
import com.hanxiaozhang.result.ResultCode;
import com.hanxiaozhang.result.Result;
import com.hanxiaozhang.security.CurrentUserUtil;
import com.hanxiaozhang.system.dao.RoleMenuDao;
import com.hanxiaozhang.system.dto.MenuDTO;
import com.hanxiaozhang.system.dto.PermissionDTO;
import com.hanxiaozhang.system.dto.RouterDTO;
import com.hanxiaozhang.system.entity.RoleMenuEntity;
import com.hanxiaozhang.util.StringUtil;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.hanxiaozhang.system.dao.MenuDao;
import com.hanxiaozhang.system.entity.MenuEntity;
import com.hanxiaozhang.system.service.MenuService;
import org.springframework.transaction.annotation.Transactional;


/**
 * 菜单表
 *
 * @author hanxiaozhang
 * @email hanxiaozhang2018@sina.com
 * @date 2021-03-30
 */
@Service
// @CacheConfig：抽取类中的所有@CachePut、@Cacheable、@CacheEvict的公共配置
@CacheConfig(cacheNames = CacheName.ROUTE_BY_USER_ID_2HOUR)
public class MenuServiceImpl implements MenuService {

    @Resource
    private MenuDao menuDao;

    @Resource
    private RoleMenuDao roleMenuDao;

    @Override
    public MenuEntity get(Long menuId) {
        return menuDao.get(menuId);
    }

    @Override
    public List<MenuDTO> list(Map<String, Object> map) {
        return menuDao.list(map);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    public int save(MenuDTO menu) {

        if (menu.getParentName().equals("无")) {
            menu.setParentId(0L);
        } else {
            menuDao.updateSubCountById(menu.getParentId(), 1);
        }

        menu.setSubCount(0)
                .setCreateBy(CurrentUserUtil.getUserName())
                .setCreateDate(LocalDateTime.now())
                .setDelFlag(DelFlagEnum.NORMAL.getCode());

        return menuDao.save(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    public int update(MenuDTO menu) {

        menu.setUpdateBy(CurrentUserUtil.getUserName())
                .setUpdateDate(LocalDateTime.now());
        return menuDao.update(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    public Result remove(Long menuId, Long parentId) {

        List<RoleMenuEntity> list = roleMenuDao.listByMenuId(menuId);
        if (!list.isEmpty()) {
            return Result.error(ResultCode.DATA_EXISTED_RELATION);
        }
        menuDao.updateSubCountById(parentId, -1);
        menuDao.remove(menuId);

        return Result.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    public Result batchRemove(Long[] menuIds, Long[] parentIds) {
        List<RoleMenuEntity> list = roleMenuDao.listByMenuIds(Arrays.asList(menuIds));
        if (!list.isEmpty()) {
            return Result.error(ResultCode.DATA_EXISTED_RELATION);
        }
        menuDao.batchRemove(menuIds);
        Map<Long, Long> idAndCountMap = Arrays.stream(parentIds)
                .collect(Collectors.groupingBy((x -> x), Collectors.counting()));
        if (!idAndCountMap.isEmpty()) {
            idAndCountMap.forEach((id, count) -> {
                menuDao.updateSubCountById(id, (int) ((-1) * count));
            });
        }
        menuDao.batchRemove(menuIds);

        return Result.success();
    }

    @Override
    @Cacheable(key = "#p0", unless = "#result == null")
    public List<RouterDTO> listRouterByUserId(Long userId) {
        List<MenuEntity> menuList = menuDao.listMenuByUserId(userId);

        List<RouterDTO> routerList = new ArrayList<>();
        if (menuList == null) {
            return routerList;
        }
        for (MenuEntity menu : menuList) {
            if (menu.getParentId() == 0) {
                routerList.add(menuToRouter(menu));
            }
        }
        listRouterRecursion(routerList, menuList);
        return routerList;
    }


    /**
     * 递归查询
     *
     * @param routerList
     * @param allMenuList
     */
    private void listRouterRecursion(List<RouterDTO> routerList, List<MenuEntity> allMenuList) {
        if (routerList != null && !routerList.isEmpty()) {
            routerList.forEach(x -> {
                Long menuId = x.getMenuId();
                List<MenuEntity> childrenMenuList = allMenuList.stream().filter(y -> menuId.equals(y.getParentId())).collect(Collectors.toList());
                List<PermissionDTO> permissionList = new ArrayList<>();
                List<RouterDTO> childrenRouterList = new ArrayList<>();
                if (x.getPermission() != null) {
                    permissionList = x.getPermission();
                }
                if (!childrenMenuList.isEmpty()) {
                    for (MenuEntity childrenMenu : childrenMenuList) {
                        if (childrenMenu.getType() == 2) {
                            permissionList.add(menuToPermission(childrenMenu));
                        } else {
                            RouterDTO routerDTO = menuToRouter(childrenMenu);
                            if (StringUtil.isNotBlank(childrenMenu.getPermission())) {
                                List<PermissionDTO> permissions = new ArrayList<>();
                                permissions.add(menuToPermission(childrenMenu));
                                routerDTO.setPermission(permissions);
                                menuToPermission(childrenMenu);
                            }

                            childrenRouterList.add(routerDTO);
                        }
                    }
                }
                x.setPermission(permissionList.isEmpty() ? null : permissionList);
                x.setRoutes(childrenMenuList.isEmpty() ? null : childrenRouterList);
                listRouterRecursion(childrenRouterList, allMenuList);
            });
        }
        return;
    }


    /**
     * 菜单转权限
     *
     * @param menu
     * @return
     */
    private PermissionDTO menuToPermission(MenuEntity menu) {
        return PermissionDTO.builder()
                .name(menu.getMenuName())
                .key(menu.getPermission())
                .build();
    }

    /**
     * 菜单转路由
     *
     * @param menu
     * @return
     */
    private RouterDTO menuToRouter(MenuEntity menu) {
        return RouterDTO.builder()
                .menuId(menu.getMenuId())
                .parentId(menu.getParentId())
                .name(menu.getMenuEnglishName())
                .path(menu.getUrl())
                .icon(StringUtil.isBlank(menu.getIcon()) ? null : menu.getIcon())
                .component(StringUtil.isBlank(menu.getComponent()) ? null : menu.getComponent())
                .build();
    }


}

